﻿using ZyGame.Execute;
using System;
using System.Collections;
using UnityEngine;

namespace ZyGame
{
    public static class RunnableExtension
    {
        public static Runnable Execute(this Runnable runnable, WaitUntil wait)
        {
            return runnable.Execute(wait);
        }

        public static Runnable<T> Execute<T>(this Runnable<T> runnable, WaitUntil wait)
        {
            return runnable.Execute(wait);
        }

        public static Runnable Execute(this Runnable builder, Running running)
        {
            return (Runnable)builder.Execute(running.Runnable);
        }

        public static Runnable<T> Execute<T>(this Runnable<T> builder, Running running)
        {
            return builder.Execute(running.Runnable);
        }

        public static Runnable<T> Execute<T, T2>(this Runnable<T> builder, Func<T2, IEnumerator> func, T2 args)
        {
            return builder.Execute(new Func<IEnumerator>(() => func(args)));
        }

        public static Runnable Execute<T2>(this Runnable builder, Func<Runnable, T2, IEnumerator> action, T2 args)
        {
            return (Runnable)builder.Execute(new Func<IEnumerator>(() => action(builder, args)));
        }

        public static Runnable<T> Execute<T, T2>(this Runnable<T> builder, Func<Runnable<T>, T2, IEnumerator> action, T2 args)
        {
            return builder.Execute(new Func<IEnumerator>(() => action(builder, args)));
        }

        public static Runnable<T> Execute<T, T2, T3>(this Runnable<T> builder, Func<Runnable<T>, T2, T3, IEnumerator> func, T2 args, T3 args2)
        {
            return builder.Execute(new Func<IEnumerator>(() => func(builder, args, args2)));
        }

        public static Runnable<T> Execute<T>(this Runnable<T> builder, Func<Runnable<T>, IEnumerator> func)
        {
            return builder.Execute(new Func<IEnumerator>(() => func(builder)));
        }

        public static Runnable<T> Execute<T, T2>(this Runnable<T> runnable, Func<T2, Runnable> next, T2 args)
        {
            return runnable.Execute(new Func<IEnumerator>(() => next(args).Waitting()));
        }

        public static Runnable<T> Execute<T>(this Runnable<T> builder, Action action)
        {
            IEnumerator Runner()
            {
                if (action != null)
                {
                    try
                    {
                        action();
                    }
                    catch (Exception e)
                    {
                        Client.Console.WriteError(e);
                        builder.SetResult(e);
                    }
                }
                yield break;
            }
            return builder.Execute(Runner);
        }

        public static Runnable<T> Execute<T, T2>(this Runnable<T> builder, Runnable<T2> executed)
        {
            return builder.Execute(executed.Waitting);
        }

        public static Runnable<T> Execute<T>(this Runnable<T> builder, Action<Runnable<T>> action)
        {
            return builder.Execute(() => { action(builder); });
        }

        public static Runnable<T> Execute<T, T2>(this Runnable<T> builder, Action<Runnable<T>, T2> action, T2 args)
        {
            return builder.Execute(() => { action(builder, args); });
        }

        public static Runnable<T> Execute<T, T2, T3>(this Runnable<T> builder, Action<T2, T3> action, T2 args, T3 args2)
        {
            return builder.Execute(() => { action(args, args2); });
        }

        public static Runnable<T> Execute<T, T2, T3>(this Runnable<T> builder, Action<Runnable<T>, Runnable<T2>, T3> func, Runnable<T2> executed, T3 args)
        {
            IEnumerator Runner()
            {
                yield return executed.Waitting();
                if (executed.EnsureExecutedSuccessful() is false)
                {
                    yield break;
                }
                try
                {
                    func(builder, executed, args);
                }
                catch (Exception e)
                {
                    Client.Console.WriteError(e);
                    builder.SetResult(e);
                }
            }
            return builder.Execute(Runner);
        }
    }
}